local skynet = require "skynet"
local gateserver = require "snax.gateserver"

--启动了一个 service/gate.lua 服务，并将处理外部连接的消息转发处理。
--[[gate 服务启动后，并非立刻开始监听。要让 gate 服务器开启监听端口，
	可以通过 lua 协议向它发送一个 open 指令，附带一个启动参数表
	
	skynet.call(gate, "lua", "open", {
    address = "127.0.0.1", -- 监听地址 127.0.0.1
	port = 8888,    -- 监听端口 8888
	maxclient = 1024,   -- 最多允许 1024 个外部连接同时建立
	nodelay = true,     -- 给外部连接设置  TCP_NODELAY 属性
})

]]

local watchdog
local connection = {}	-- fd -> connection : { fd , client, agent , ip, mode }
local forwarding = {}	-- agent -> connection

skynet.register_protocol {
	name = "client",
	id = skynet.PTYPE_CLIENT,
}

local handler = {}

--监听端口做初始化的操作，配置gate参数表
function handler.open(source, conf)
	watchdog = conf.watchdog or source
end

--当一个完整的包被切分好后，message 方法被调用
--msg c指针
--sz 数字，包长度
function handler.message(fd, msg, sz)
	-- recv a package, forward it
	local c = connection[fd]
	local agent = c.agent

	--调用完毕后处理c指针的两种方法
	if agent then --1.转给别的服务处理
		-- It's safe to redirect msg directly , gateserver framework will not free msg.
		--转到agent服务去处理
		skynet.redirect(agent, c.client, "client", fd, msg, sz)
	else--2.调用skynet_free 处理，调用底层来处理 netpack.tostring（库）
		skynet.send(watchdog, "lua", "socket", "data", fd, skynet.tostring(msg, sz))
		-- skynet.tostring will copy msg to a string, so we must free msg here.

		--位置:lualib-src中lua-skynet.c static int ltrash(lua_State *L)
		skynet.trash(msg,sz)
	end
end

--accept后被调用 如：127.0.0.1:8000
--socketfd + clientipaddr
function handler.connect(fd, addr)
	local c = {
		fd = fd,
		ip = addr,
	}
	connection[fd] = c
	skynet.send(watchdog, "lua", "socket", "open", fd, addr)
end

local function unforward(c)
	if c.agent then
		forwarding[c.agent] = nil
		c.agent = nil
		c.client = nil
	end
end

local function close_fd(fd)
	local c = connection[fd]
	if c then
		unforward(c)
		connection[fd] = nil
	end
end

--踢掉fd
function handler.disconnect(fd)
	close_fd(fd)
	skynet.send(watchdog, "lua", "socket", "close", fd)
end

function handler.error(fd, msg)
	close_fd(fd) --connect表中对应的socket fd 设置为nil
	skynet.send(watchdog, "lua", "socket", "error", fd, msg) --返回消息msg
end

--当 fd 上待发送的数据累积超过 1M 字节后,触发
function handler.warning(fd, size)
	skynet.send(watchdog, "lua", "socket", "warning", fd, size)
end

local CMD = {}

function CMD.forward(source, fd, client, address)
	local c = assert(connection[fd])
	unforward(c)
	c.client = client or 0
	c.agent = address or source
	forwarding[c.agent] = c
	gateserver.openclient(fd)
end

--当一个新客户端被accept之后，回调connect方法
function CMD.accept(source, fd)
	local c = assert(connection[fd])
	unforward(c)
	gateserver.openclient(fd)
end

function CMD.kick(source, fd)
	gateserver.closeclient(fd)
end

--让服务处理一些 skynet 内部消息，注册command方法，source是消息来源
--返回值会通过 skynet.ret/skynet.pack 返回给来源服务
function handler.command(cmd, source, ...)
	local f = assert(CMD[cmd])
	return f(source, ...)
end

gateserver.start(handler)
